/**
 * Copyright (C) 2006 OQube 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 * author: Arnaud Bailly - abailly AT oqube DOT com
 * created: 13 juin 2006
 */
package speculoos.jndi;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.apache.commons.collections.MapUtils;

import speculoos.beans.GeneratorException;

/**
 * This class handles code generation for bean classes described in a
 * configuration file associated with mapping files.
 * <p>
 * This class can generate (at least) three different java source files given a
 * {@see java.lang.Class} object and a {@see java.util.Map} object:
 * <ol>
 * <li>A mapper that produces a LDAP filter query with equality predicates for
 * all non null values in the instance given,</li>
 * <li>A mapper that produces an {@see javax.naming.directory.Attributes} object
 * from an instance of the given class,</li>
 * <li>A mapper that produces an instance of the given class given an
 * {@see javax.naming.directory.Attributes} object.</li>
 * </ol>
 * The first one is used in producing "automatic" filters for
 * {@see speculoos.jndi.SearchMapper} instances, the second one for
 * {@see speculoos.jndi.AddMapper} and the last one for producing objects from
 * the result of a {@see speculoos.jndi.SearchMapper}.
 * </p>
 * <p>
 * The kind of code generated depends on the {@see #name} attribute of this
 * generator which must be a template relative to <code>templates/mappers</code>
 * path.
 * </p>
 * 
 * @author nono
 * 
 */
public class MapsBeansGenerator {

	/*
	 * where data is written to.
	 */
	private Writer output;

	/*
	 * package name
	 */
	private String pack;

	/*
	 * an identifier associated with this generator.
	 */
	private String name;

	/**
	 * Generate interface and implementation for given map and class object.
	 * 
	 * @param map
	 *            a map from LDAP to Java names.
	 * @throws IOException
	 * @throws GeneratorException
	 */
	public void generate(Class cls, Map map) throws GeneratorException {
		/* extract bean info */
		BeanInfo bean = null;
		try {
			bean = Introspector.getBeanInfo(cls);
			PropertyDescriptor[] props = bean.getPropertyDescriptors();
			/* distinguishes scalars from containers */
			List scalars = new ArrayList();
			List containers = new ArrayList();
			List primitives = new ArrayList();
			for (int i = 0; i < props.length; i++) {
				if (Collection.class.isAssignableFrom(props[i]
						.getPropertyType()))
					containers.add(props[i]);
				else if(props[i].getPropertyType().isPrimitive())
					primitives.add(props[i]);
				else
					scalars.add(props[i]);
			}
			StringTemplateGroup grp = new StringTemplateGroup("beanmaps");
			StringTemplate iftpl = grp.getInstanceOf("templates/mappers/"
					+ name);
			iftpl.setAttribute("map", MapUtils.invertMap(map));
			iftpl.setAttribute("scalars", scalars);
			iftpl.setAttribute("containers", containers);
			iftpl.setAttribute("primitives", primitives);
			iftpl.setAttribute("bean", bean);
			iftpl.setAttribute("simpleName", cls.getName().substring(cls.getPackage().getName().length() + 1 ));
			iftpl.setAttribute("pack", getPack());
			
			setCommons(iftpl);
			output.write(iftpl.toString());
		} catch (IntrospectionException e) {
			throw new GeneratorException(getName()
					+ ": cannot extract javabeans information from " + cls, e);
		} catch (IOException e) {
			throw new GeneratorException(getName()
					+ ": error while writing to output stream for " + cls, e);
		}
	}

	private void setCommons(StringTemplate iftpl) {
		iftpl.setAttribute("date", new Date());
		iftpl.setAttribute("from", name);
		iftpl.setAttribute("package", pack);
	}

	/**
	 * @return Returns the outputImpl.
	 */
	public Writer getOutput() {
		return output;
	}

	/**
	 * @param outputImpl
	 *            The outputImpl to set.
	 */
	public void setOutput(Writer output) {
		this.output = output;
	}

	/**
	 * @return Returns the pack.
	 */
	public String getPack() {
		return pack;
	}

	/**
	 * @param pack
	 *            The pack to set.
	 */
	public void setPack(String pack) {
		this.pack = pack;
	}

	/**
	 * @return Returns the name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            The name to set.
	 */
	public void setName(String name) {
		this.name = name;
	}
}
